/*
 * [y] hybris Platform
 *
 * Copyright (c) 2000-2012 hybris AG
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of hybris
 * ("Confidential Information"). You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the
 * license agreement you entered into with hybris.
 * 
 *  
 */
package de.hybris.merchandise.storefront.controllers.pages;

import de.hybris.platform.acceleratorfacades.storefinder.StoreFinderFacade;
import de.hybris.platform.acceleratorservices.storefinder.data.StoreFinderSearchPageData;
import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException;
import de.hybris.platform.cms2.model.pages.AbstractPageModel;
import de.hybris.platform.cms2.model.pages.ContentPageModel;
import de.hybris.platform.commercefacades.storelocator.data.PointOfServiceData;
import de.hybris.platform.commerceservices.search.pagedata.PageableData;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.storelocator.exception.GeoLocatorException;
import de.hybris.platform.storelocator.exception.MapServiceException;
import de.hybris.merchandise.storefront.breadcrumb.impl.StorefinderBreadcrumbBuilder;
import de.hybris.merchandise.storefront.constants.WebConstants;
import de.hybris.merchandise.storefront.controllers.ControllerConstants;
import de.hybris.merchandise.storefront.controllers.util.GlobalMessages;
import de.hybris.merchandise.storefront.forms.StoreFinderForm;
import de.hybris.merchandise.storefront.forms.StorePositionForm;
import de.hybris.merchandise.storefront.util.MetaSanitizerUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.UriComponentsBuilder;


/**
 * Controller for store locator search and detail pages. Provides display data for these two pages. Search result amount
 * is limited to the {@link StoreLocatorPageController#INITIAL_LOCATIONS_TO_DISPLAY} value. Increasing number of
 * displayed stores is possible by giving proper argument for
 * {@link StoreLocatorPageController#findSores(String, int, de.hybris.merchandise.storefront.forms.StoreFinderForm, org.springframework.ui.Model, org.springframework.validation.BindingResult)}
 * method but is limited to max value of {@link StoreLocatorPageController#MAX_LOCATIONS_TO_DISPLAY} stores.
 */
@Controller
@Scope("tenant")
@RequestMapping(value = "/store-finder")
public class StoreLocatorPageController extends AbstractPageController
{
	protected static final Logger LOG = Logger.getLogger(StoreLocatorPageController.class);

	private static final String STORE_FINDER_CMS_PAGE_LABEL = "storefinder";
	private static final String GOOGLE_API_KEY_ID = "googleApiKey";
	private static final String GOOGLE_API_VERSION = "googleApiVersion";

	private static final int INITIAL_LOCATIONS_TO_DISPLAY = 5;
	private static final int LOCATIONS_INCREMENT = 5;
	private static final int MAX_LOCATIONS_TO_DISPLAY = 100;
	private static final String INITIAL_LOCATIONS_TO_DISPLAY_TEXT = "" + INITIAL_LOCATIONS_TO_DISPLAY; //NOPMD

	@Resource(name = "configurationService")
	private ConfigurationService configurationService;

	@Resource(name = "storefinderBreadcrumbBuilder")
	private StorefinderBreadcrumbBuilder storefinderBreadcrumbBuilder;

	@Resource(name = "storeFinderFacade")
	private StoreFinderFacade storeFinderFacade;


	@ModelAttribute("googleApiVersion")
	public String getGoogleApiVersion()
	{
		return configurationService.getConfiguration().getString(GOOGLE_API_VERSION);
	}

	@ModelAttribute("googleApiKey")
	public String getGoogleApiKey(final HttpServletRequest request)
	{
		final String googleApiKey = getHostConfigService().getProperty(GOOGLE_API_KEY_ID, request.getServerName());
		if (StringUtils.isEmpty(googleApiKey))
		{
			LOG.warn("No Google API key found for server: " + request.getServerName());
		}
		return googleApiKey;
	}

	// Method to get the empty search form
	@RequestMapping(method = RequestMethod.GET)
	public String getStoreFinderPage(final Model model) throws CMSItemNotFoundException
	{
		setUpPageForms(model);
		model.addAttribute(WebConstants.BREADCRUMBS_KEY, storefinderBreadcrumbBuilder.getBreadcrumbs());
		storeCmsPageInModel(model, getStoreFinderPage());
		setUpMetaDataForContentPage(model, (ContentPageModel) getStoreFinderPage());
		return ControllerConstants.Views.Pages.StoreFinder.StoreFinderSearchPage;
	}

	@RequestMapping(method = RequestMethod.GET, params = "q")
	public String findSores(
			@RequestParam(value = "q") final String locationQuery,
			@RequestParam(value = "more", defaultValue = INITIAL_LOCATIONS_TO_DISPLAY_TEXT, required = false) final int requestedPageSize,
			final StoreFinderForm storeFinderForm, final Model model, final BindingResult bindingResult) throws GeoLocatorException,
			MapServiceException, CMSItemNotFoundException
	{
		if (StringUtils.isBlank(locationQuery))
		{
			setUpPageForms(model);
			GlobalMessages.addErrorMessage(model, "storelocator.error.no.results.subtitle");
			model.addAttribute(WebConstants.BREADCRUMBS_KEY,
					storefinderBreadcrumbBuilder.getBreadcrumbsForLocationSearch(locationQuery));
			storeCmsPageInModel(model, getStoreFinderPage());
			return ControllerConstants.Views.Pages.StoreFinder.StoreFinderSearchPage;
		}

		final int pageSize = Math.min(MAX_LOCATIONS_TO_DISPLAY, requestedPageSize);

		// Run the location search & populate the model
		runLocationSearch(locationQuery, model, pageSize);
		model.addAttribute("metaRobots", "no-index,follow");
		final String metaKeywords = MetaSanitizerUtil.sanitizeKeywords(locationQuery);
		final String metaDescription = MetaSanitizerUtil.sanitizeDescription(getSiteName() + " "
				+ getMessageSource().getMessage("storeFinder.meta.description.results", null, getI18nService().getCurrentLocale())
				+ " " + locationQuery);
		setUpMetaData(model, metaKeywords, metaDescription);
		updatePageTitle(locationQuery, model);
		return ControllerConstants.Views.Pages.StoreFinder.StoreFinderSearchPage;
	}

	private void runLocationSearch(final String locationQuery, final Model model, final int pageSize)
			throws CMSItemNotFoundException
	{
		// Run the location search & populate the model
		final PageableData pageableData = preparePage(pageSize);
		final StoreFinderSearchPageData<PointOfServiceData> searchResult = storeFinderFacade.locationSearch(locationQuery,
				pageableData);
		model.addAttribute("storeSearchPageData", searchResult);
		model.addAttribute("locationQuery", StringEscapeUtils.escapeHtml(searchResult.getLocationText()));
		if (searchResult.getResults().isEmpty())
		{
			GlobalMessages.addErrorMessage(model, "storelocator.error.no.results.subtitle");
		}

		model.addAttribute(WebConstants.BREADCRUMBS_KEY,
				storefinderBreadcrumbBuilder.getBreadcrumbsForLocationSearch(locationQuery));
		storeCmsPageInModel(model, getStoreFinderPage());

		if (isLimitNotExceeded(searchResult))
		{
			final int nextMoreSize = searchResult.getPagination().getPageSize() + LOCATIONS_INCREMENT;
			final String showMoreUrl = UriComponentsBuilder.fromPath("/store-finder").queryParam("q", locationQuery)
					.queryParam("more", Integer.valueOf(nextMoreSize)).build().toString();
			model.addAttribute("showMoreUrl", showMoreUrl);
		}
	}

	private void setUpPageForms(final Model model)
	{
		final StoreFinderForm storeFinderForm = new StoreFinderForm();
		model.addAttribute("storeFinderForm", storeFinderForm);
		final StorePositionForm storePositionForm = new StorePositionForm();
		model.addAttribute("storePositionForm", storePositionForm);
	}

	@RequestMapping(value = "/position", method =
	{ RequestMethod.GET, RequestMethod.POST })
	public String searchByCurrentPosition(
			final StorePositionForm storePositionForm,
			@RequestParam(value = "more", defaultValue = INITIAL_LOCATIONS_TO_DISPLAY_TEXT, required = false) final int requestedPageSize,
			final Model model) throws GeoLocatorException, MapServiceException, CMSItemNotFoundException
	{
		final int pageSize = Math.min(MAX_LOCATIONS_TO_DISPLAY, requestedPageSize);

		// Run the location search & populate the model
		final PageableData pageableData = preparePage(pageSize);
		final StoreFinderSearchPageData<PointOfServiceData> searchResult = storeFinderFacade.positionSearch(
				storePositionForm.getLatitude(), storePositionForm.getLongitude(), pageableData);
		model.addAttribute("storeSearchPageData", searchResult);
		model.addAttribute("locationQuery", StringEscapeUtils.escapeHtml(searchResult.getLocationText()));

		model.addAttribute(WebConstants.BREADCRUMBS_KEY, storefinderBreadcrumbBuilder.getBreadcrumbsForCurrentPositionSearch());
		storeCmsPageInModel(model, getStoreFinderPage());
		setUpPageForms(model);


		if (isLimitNotExceeded(searchResult))
		{
			final int nextMoreSize = searchResult.getPagination().getPageSize() + LOCATIONS_INCREMENT;
			final String showMoreUrl = UriComponentsBuilder.fromPath("/store-finder/position")
					.queryParam("lat", Double.valueOf(storePositionForm.getLongitude()))
					.queryParam("long", Double.valueOf(storePositionForm.getLatitude()))
					.queryParam("more", Integer.valueOf(nextMoreSize)).build().toString();
			model.addAttribute("showMoreUrl", showMoreUrl);
		}

		return ControllerConstants.Views.Pages.StoreFinder.StoreFinderSearchPage;
	}

	protected PageableData preparePage(final int pageSize)
	{
		final PageableData pageableData = new PageableData();
		pageableData.setCurrentPage(0); // Always on the first page
		pageableData.setPageSize(pageSize); // Adjust pageSize to see more
		return pageableData;
	}

	private boolean isLimitNotExceeded(final StoreFinderSearchPageData<PointOfServiceData> searchResult)
	{
		return searchResult.getPagination().getNumberOfPages() > 1
				&& searchResult.getPagination().getPageSize() < MAX_LOCATIONS_TO_DISPLAY;
	}

	protected AbstractPageModel getStoreFinderPage() throws CMSItemNotFoundException
	{
		return getContentPageForLabelOrId(STORE_FINDER_CMS_PAGE_LABEL);
	}

	protected void updatePageTitle(final String searchText, final Model model)
	{
		storeContentPageTitleInModel(
				model,
				getPageTitleResolver().resolveContentPageTitle(
						getMessageSource().getMessage("storeFinder.meta.title", null, getI18nService().getCurrentLocale()) + " "
								+ searchText));
	}
}
